﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using Chefbook.HelperMethods;
using Chefbook.DataAccessLayer;

namespace Chefbook.DataObjects
{
    public class Recipe
    {
        /// <summary>
        /// Internal to allow cleanup without infinite loop.  Do not use.
        /// </summary>
        internal List<Category> categories = new List<Category>();
        private List<Ingredient> ingredients = new List<Ingredient>();
        private string name, description, directions;
        private readonly int id;

        /// <summary>
        /// Creates a new Recipe in the specified category
        /// </summary>
        /// <param name="parent"></param>
        public Recipe(Category parent, int id)
        {
            parent.recipes.AddIfNew(this);
            this.AddCategory(parent);
            Name = string.Empty;
            Description = string.Empty;
            Directions = string.Empty;
            this.id = id;
        }

        /// <summary>
        /// Gets the Id of this Recipe
        /// </summary>
        public int Id
        {
            get { return id; }
        }

        /// <summary>
        /// Gets or sets the Name of the Recipe
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value.ThrowIfNull("Name"); }
        }

        /// <summary>
        /// Gets or sets the Description of the Recipe
        /// </summary>
        public string Description
        {
            get { return description; }
            set { description = value.ThrowIfNull("Description"); }
        }

        /// <summary>
        /// Gets or sets the Directions for how to make the Recipe
        /// </summary>
        public string Directions {
            get { return directions; }
            set { directions = value.ThrowIfNull("Description"); }
        }

        /// <summary>
        /// Gets or sets how much preparation time the Recipe requires
        /// </summary>
        public TimeSpan PrepTime { get; set; }

        /// <summary>
        /// Gets or sets how long the Recipe needs to cook
        /// </summary>
        public TimeSpan CookTime { get; set; }

        /// <summary>
        /// Gets a readonlylist of categories this recipe belongs to
        /// </summary>
        public ReadOnlyCollection<Ingredient> Ingredients
        {
            get { return new ReadOnlyCollection<Ingredient>(ingredients); }
        }

        /// <summary>
        /// Gets a readonlylist of categories this recipe belongs to
        /// </summary>
        public ReadOnlyCollection<Category> Categories
        {
            get { return new ReadOnlyCollection<Category>(categories); }
        }

        /// <summary>
        /// Removes this recipe from all categories
        /// </summary>
        public void Delete()
        {
            var tempCategories = new List<Category>(categories);
            foreach (var cat in tempCategories)
            {
                cat.Recipes.Remove(this);
            }
        }

        /// <summary>
        /// Moves a recipe from one category to another
        /// </summary>
        /// <param name="from">The originating category</param>
        /// <param name="to">The category to move to</param>
        public void ChangeCategory(Category from, Category to)
        {
            from.ThrowIfNull("from");
            to.ThrowIfNull("to");

            to.Recipes.Add(this);
            from.Recipes.Remove(this);
        }

        /// <summary>
        /// Adds this recipe to a category
        /// </summary>
        /// <param name="categoryToAdd"></param>
        public void AddCategory(Category categoryToAdd)
        {
            categoryToAdd.ThrowIfNull("categoryToAdd");
            categories.AddIfNew(categoryToAdd);
            categoryToAdd.recipes.AddIfNew(this);
        }

        /// <summary>
        /// Removes this recipe from a category
        /// </summary>
        /// <param name="categoryToRemove">The category to remove from</param>
        public void RemoveCategory(Category categoryToRemove)
        {
            categoryToRemove.ThrowIfNull("categoryToRemove");
            ParameterValidation.ValidateArgument(() => categories.Contains(categoryToRemove), "categoryToRemove");
            categories.Remove(categoryToRemove);
            categoryToRemove.recipes.Remove(this);
        }

        /// <summary>
        /// Adds an ingredient to the recipe
        /// </summary>
        /// <param name="ingredientToAdd">The ingredient to add</param>
        public Ingredient AddNewIngredient()
        {
            var ing = new Ingredient(this);
            ingredients.AddIfNew(ing);
            return ing;
        }

        /// <summary>
        /// Removes an ingredient from the recipe
        /// </summary>
        /// <param name="ingredientToRemove"></param>
        public void RemoveIngredient(Ingredient ingredientToRemove)
        {
            ingredientToRemove.ThrowIfNull("ingredientToRemove");
            ParameterValidation.ValidateArgument(() => ingredients.Contains(ingredientToRemove), "ingredientToRemove");
            ingredients.Remove(ingredientToRemove);
        }

        /// <summary>
        /// Returns the name of the recipe
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Name;
        }
    }
}
